# // Package context provides single entry to all resources
# package context

# import (
# 	gocontext "context"
# 	"fmt"
# 	"math/rand"
# 	"os"
# 	"os/signal"
# 	"path/filepath"
# 	"runtime"
# 	"runtime/pprof"
# 	"strings"
# 	"sync"
# 	"time"

# 	"github.com/aptly-dev/aptly/aptly"
# 	"github.com/aptly-dev/aptly/azure"
# 	"github.com/aptly-dev/aptly/console"
# 	"github.com/aptly-dev/aptly/database"
# 	"github.com/aptly-dev/aptly/database/goleveldb"
# 	"github.com/aptly-dev/aptly/deb"
# 	"github.com/aptly-dev/aptly/files"
# 	"github.com/aptly-dev/aptly/http"
# 	"github.com/aptly-dev/aptly/pgp"
# 	"github.com/aptly-dev/aptly/s3"
# 	"github.com/aptly-dev/aptly/swift"
# 	"github.com/aptly-dev/aptly/task"
# 	"github.com/aptly-dev/aptly/utils"
# 	"github.com/smira/commander"
# 	"github.com/smira/flag"
# )
from files.public import *
from files.package_pool import *
import files
from typing import List,Dict
from utils import config as cfg
from deb.listp import *
from deb.collections import *
import database
from pgp.gnupg_finder import *
from pgp.gnupg import *
# // AptlyContext is a common context shared by all commands
class AptlyContext:

    flags ={}
    globalFlags ={} #*flag.FlagSet
    configLoaded       :bool=False

    progress          =None
    downloader        =None
    taskList          =None
    database          =None
    packagePool       :PackagePool=None
    publishedStorages :Dict[str,PublishedStorage]={}
    dependencyOptions :int=-1
    architecturesList :List[str]=[]
    # // Debug features
    # fileCPUProfile *os.File
    # fileMemProfile *os.File
    # fileMemStats   *os.File



    # // Config loads and returns current configuration
    def  Config(context)->cfg.ConfigStructure:

        return context.config()
    

    def config(context)->cfg.ConfigStructure:
        if not context.configLoaded :

            configLocation = context.globalFlags.get('config')
            if configLocation is not None  :
                cfg.Config,err = cfg.LoadConfig(configLocation)

                
            else :
                configLocations = [os.path.join(os.getenv("HOME"), ".aptly.conf"),
                    "/etc/aptly.conf",]
                    
                

                for  configLocation in configLocations :
                    cfg.Config,err = cfg.LoadConfig(configLocation)
                    if err is None  :
                        break
                    
                # 	if !os.IsNotExist(err) {
                # 		Fatal(fmt.Errorf("error loading config file %s: %s", configLocation, err))
                # 	}
                # }

                # if err is not None  
                # 	fmt.Fprintf(os.Stderr, "Config file not found, creating default config at %s\n\n", configLocations[0])

                # 	# // as this is fresh aptly installation, we don't need to support legacy pool locations
                cfg.Config.SkipLegacyPool = True
                # raise ''
                cfg.SaveConfig(configLocations[0],cfg.Config)
            context.configLoaded = True

        
        return cfg.Config
    def LookupOption(context,defaultValue :bool, name :str) ->bool :
        # context.Lock()
        # defer context.Unlock()

        return context.lookupOption(defaultValue, name)
    

    def  lookupOption(context,defaultValue :bool, name :str) -> bool :
        result = defaultValue

        if context.globalFlags.get(name) :
            result = bool(context.globalFlags)
        

        return result
        
    # // DependencyOptions calculates options related to dependecy handling
    def  DependencyOptions(context) ->int :


        if context.dependencyOptions == -1 :
            context.dependencyOptions = 0
            if context.lookupOption(context.config().DepFollowSuggests, "dep-follow-suggests") :
                context.dependencyOptions |= DepFollow.DepFollowSuggests
            
            if context.lookupOption(context.config().DepFollowRecommends, "dep-follow-recommends") :
                context.dependencyOptions |= DepFollow.DepFollowRecommends
            
            if context.lookupOption(context.config().DepFollowAllVariants, "dep-follow-all-variants") :
                context.dependencyOptions |= DepFollow.DepFollowAllVariants
            
            if context.lookupOption(context.config().DepFollowSource, "dep-follow-source") :
                context.dependencyOptions |= DepFollow.DepFollowSource
            
            if context.lookupOption(context.config().DepVerboseResolve, "dep-verbose-resolve") :
                context.dependencyOptions |= DepFollow.DepVerboseResolve
            

        return context.dependencyOptions
    

    # // ArchitecturesList returns list of architectures fixed via command line or config
    def ArchitecturesList(context)-> List[str]: 

        if context.architecturesList is None  :
            context.architecturesList = context.config().Architectures
            optionArchitectures = context.globalFlags.get("architectures")
            if optionArchitectures != "" :
                context.architecturesList = optionArchitectures.split( ",")


        return context.architecturesList
    
    # // PackagePool returns instance of PackagePool
    def PackagePool(context) ->PackagePool:
        if context.packagePool is None:
            context.packagePool = NewPackagePool(context.config().RootDir, not context.config().SkipLegacyPool)
        return context.packagePool




    # // GetPublishedStorage returns instance of PublishedStorage
    def GetPublishedStorage(context,name :str) ->PublishedStorage :


        publishedStorage= context.publishedStorages.get(name)
        if publishedStorage is None:
            if name == "" :
                publishedStorage = NewPublishedStorage(os.path.join(context.config().RootDir, "public"), "hardlink", "")
            elif name.startswith( "filesystem:") :

                params= context.config().FileSystemPublishEndpoints[name[11:]]
                if params is None:
                   raise Exception("published local storage {} not configured".format( name[11:]))
                

                publishedStorage = NewPublishedStorage(params.get('RootDir'), params.get('LinkMethod'), params.get('VerifyMethod'))
            # 暂时屏蔽除本地之外的文件系统
            # } else if strings.HasPrefix(name, "s3:") {
            #     params, ok = context.config().S3PublishRoots[name[3:]]
            #     if !ok {
            #         Fatal(fmt.Errorf("published S3 storage %v not configured", name[3:]))
            #     }

            #     var err error
            #     publishedStorage, err = s3.NewPublishedStorage(
            #         params.AccessKeyID, params.SecretAccessKey, params.SessionToken,
            #         params.Region, params.Endpoint, params.Bucket, params.ACL, params.Prefix, params.StorageClass,
            #         params.EncryptionMethod, params.PlusWorkaround, params.DisableMultiDel,
            #         params.ForceSigV2, params.ForceVirtualHostedStyle, params.Debug)
            #     if err is not None  {
            #         Fatal(err)
            #     }
            # } else if strings.HasPrefix(name, "swift:") {
            #     params, ok = context.config().SwiftPublishRoots[name[6:]]
            #     if !ok {
            #         Fatal(fmt.Errorf("published Swift storage %v not configured", name[6:]))
            #     }

            #     var err error
            #     publishedStorage, err = swift.NewPublishedStorage(params.UserName, params.Password,
            #         params.AuthURL, params.Tenant, params.TenantID, params.Domain, params.DomainID, params.TenantDomain, params.TenantDomainID, params.Container, params.Prefix)
            #     if err is not None  {
            #         Fatal(err)
            #     }
            # } else if strings.HasPrefix(name, "azure:") {
            #     params, ok = context.config().AzurePublishRoots[name[6:]]
            #     if !ok {
            #         Fatal(fmt.Errorf("Published Azure storage %v not configured", name[6:]))
            #     }

            #     var err error
            #     publishedStorage, err = azure.NewPublishedStorage(
            #         params.AccountName, params.AccountKey, params.Container, params.Prefix, params.Endpoint)
            #     if err is not None  {
            #         Fatal(err)
            #     }
            else :
                raise Exception("unknown published storage format: {}".format(name))
            
            context.publishedStorages[name] = publishedStorage
        

        return publishedStorage
    

    # // UploadPath builds path to upload storage
    def UploadPath(context) ->str :
        return os.path.join(context.Config().RootDir, "upload")
    

    # // UpdateFlags sets internal copy of flags in the context
    def UpdateFlags(context,flags :dict) :
        context.flags = flags
    

    # // Flags returns current command flags
    def  Flags(context) ->dict:
        return context.flags


    # // GlobalFlags returns flags passed to all commands
    def  GlobalFlags(context) ->dict:
        
        return context.globalFlags
    
    # // NewCollectionFactory builds factory producing all kinds of collections
    def NewCollectionFactory(context) :

        db= context._database()
        return NewCollectionFactory(db)


    def  _database(context) :
        if context.database is None :
            context.database = database.database.NewDB(context.dbPath())
        return context.database

    # // DBPath builds path to database
    def  DBPath(context) ->str:
        
        return context.dbPath()
    
    # // DBPath builds path to database
    def  dbPath(context) ->str :
        try:
            os.makedirs(os.path.join(context.config().RootDir, "db"),mode=0o777)
        except:
            pass
        return os.path.join(context.config().RootDir, "db",'aptly.db')
    # // GetVerifier returns Verifier with respect to provider
    def  GetVerifier(context) :


        provider = context.pgpProvider()
        if provider == "internal" : #// nolint: goconst
            return None#&pgp.GoVerifier{}
        

        return None#pgp.NewGpgVerifier(context.getGPGFinder(provider))
    
    # // GetSigner returns Signer with respect to provider
    def  GetSigner(context) :
     
        provider = context.pgpProvider()
        print('-------',provider)
        if provider == "internal" :# // nolint: goconst
            return None#&pgp.GoSigner{}
        

        return  NewGpgSigner(context.getGPGFinder(provider))
    def pgpProvider(context) ->str:

        if context.globalFlags.get("gpg-provider") :
            provider = context.globalFlags.get("gpg-provider") 
        else :
            provider = context.config().GpgProvider
        

        providerList=["gpg","gpg1","gpg2","internal"]
        if provider not in providerList:
        
            raise Exception("unknown gpg provider: {}".format( provider))
        

        return provider
    # }


    def  getGPGFinder(context) :
        pgpProvider=context.pgpProvider() 
        if pgpProvider== "gpg1":
            return   GPG1Finder()
        elif pgpProvider== "gpg2":
            return   GPG2Finder()
        elif pgpProvider== "gpg":
            return  GPGDefaultFinder()
        

        raise ("uknown GPG provider type")

# // Check interface
# var _ aptly.PublishedStorageProvider = &AptlyContext{}

# // FatalError is type for panicking to abort execution with non-zero
# // exit code and print meaningful explanation
# type FatalError struct {
# 	ReturnCode int
# 	Message    string
# }

# // Fatal panics and aborts execution with exit code 1
# def Fatal(err error) {
# 	returnCode = 1
# 	if err == commander.ErrFlagError || err == commander.ErrCommandError {
# 		returnCode = 2
# 	}
# 	raise (&FatalError{ReturnCode: returnCode, Message: err.Error()})
# }


# // LookupOption checks boolean flag with default (usually config) and command-line
# // setting


# // Progress creates or returns Progress object
# def (context *AptlyContext) Progress() aptly.Progress {
# 	context.Lock()
# 	defer context.Unlock()

# 	return context._progress()
# }

# def (context *AptlyContext) _progress() aptly.Progress {
# 	if context.progress is  None  {
# 		context.progress = console.NewProgress()
# 		context.progress.Start()
# 	}

# 	return context.progress
# }





# // NewDownloader returns instance of new downloader with given progress
# def (context *AptlyContext) NewDownloader(progress aptly.Progress) aptly.Downloader {
# 	context.Lock()
# 	defer context.Unlock()

# 	return context.newDownloader(progress)
# }

# // NewDownloader returns instance of new downloader with given progress without locking
# // so it can be used for internal usage.
# def (context *AptlyContext) newDownloader(progress aptly.Progress) aptly.Downloader {
# 	var downloadLimit int64
# 	limitFlag = context.flags.Lookup("download-limit")
# 	if limitFlag is not None  {
# 		downloadLimit = limitFlag.Value.Get().(int64)
# 	}
# 	if downloadLimit == 0 {
# 		downloadLimit = context.config().DownloadLimit
# 	}
# 	maxTries = context.config().DownloadRetries + 1
# 	maxTriesFlag = context.flags.Lookup("max-tries")
# 	if maxTriesFlag is not None  {
# 		// If flag is defined prefer it to global setting
# 		maxTries = maxTriesFlag.Value.Get().(int)
# 	}
# 	var downloader string = context.config().Downloader
# 	downloaderFlag = context.flags.Lookup("downloader")
# 	if downloaderFlag is not None  {
# 		downloader = downloaderFlag.Value.String()
# 	}

# 	if downloader == "grab" {
# 		return http.NewGrabDownloader(downloadLimit*1024, maxTries, progress)
# 	}
# 	return http.NewDownloader(downloadLimit*1024, maxTries, progress)
# }

# // Downloader returns instance of current downloader
# def (context *AptlyContext) Downloader() aptly.Downloader {
# 	context.Lock()
# 	defer context.Unlock()

# 	if context.downloader is  None  {
# 		context.downloader = context.newDownloader(context._progress())
# 	}

# 	return context.downloader
# }

# // TaskList returns instance of current task list
# def (context *AptlyContext) TaskList() *task.List {
# 	context.Lock()
# 	defer context.Unlock()

# 	if context.taskList is  None  {
# 		context.taskList = task.NewList()
# 	}
# 	return context.taskList
# }





# // Database opens and returns current instance of database
# def (context *AptlyContext) Database() (database.Storage, error) {
# 	context.Lock()
# 	defer context.Unlock()

# 	return context._database()
# }



# // CloseDatabase closes the db temporarily
# def (context *AptlyContext) CloseDatabase() error {
# 	context.Lock()
# 	defer context.Unlock()

# 	if context.database is  None  {
# 		return   None 
# 	}

# 	return context.database.Close()
# }

# // ReOpenDatabase reopens the db after close
# def (context *AptlyContext) ReOpenDatabase() error {
# 	_, err = context.Database()

# 	return err
# }









# // GoContextHandleSignals upgrades context to handle ^C by aborting context
# def (context *AptlyContext) GoContextHandleSignals() {
# 	context.Lock()
# 	defer context.Unlock()

# 	// Catch ^C
# 	sigch = make(chan os.Signal, 1)
# 	signal.Notify(sigch, os.Interrupt)

# 	var cancel gocontext.Canceldef

# 	context.Context, cancel = gocontext.WithCancel(context.Context)

# 	go def() {
# 		<-sigch
# 		signal.Stop(sigch)
# 		context.Progress().PrintfStdErr("Aborting... press ^C once again to abort immediately\n")
# 		cancel()
# 	}()
# }

# // Shutdown shuts context down
# def (context *AptlyContext) Shutdown() {
# 	context.Lock()
# 	defer context.Unlock()

# 	if aptly.EnableDebug {
# 		if context.fileMemProfile is not None  {
# 			pprof.WriteHeapProfile(context.fileMemProfile)
# 			context.fileMemProfile.Close()
# 			context.fileMemProfile =   None 
# 		}
# 		if context.fileCPUProfile is not None  {
# 			pprof.StopCPUProfile()
# 			context.fileCPUProfile.Close()
# 			context.fileCPUProfile =   None 
# 		}
# 		if context.fileMemProfile is not None  {
# 			context.fileMemProfile.Close()
# 			context.fileMemProfile =   None 
# 		}
# 	}
# 	if context.database is not None  {
# 		context.database.Close()
# 		context.database =   None 
# 	}
# 	if context.downloader is not None  {
# 		context.downloader =   None 
# 	}
# 	if context.progress is not None  {
# 		context.progress.Shutdown()
# 		context.progress =   None 
# 	}
# }

# // Cleanup does partial shutdown of context
# def (context *AptlyContext) Cleanup() {
# 	context.Lock()
# 	defer context.Unlock()

# 	if context.downloader is not None  {
# 		context.downloader =   None 
# 	}
# 	if context.progress is not None  {
# 		context.progress.Shutdown()
# 		context.progress =   None 
# 	}
# }

# // NewContext initializes context with default settings
# def NewContext(flags *flag.FlagSet) (*AptlyContext, error) {
# 	var err error

# 	context = new AptlyContext{
# 		flags:             flags,
# 		globalFlags:       flags,
# 		dependencyOptions: -1,
        
# 		publishedStorages: map[string]aptly.PublishedStorage{},
# 	}


# 	return context,   None 
# }
